<?php

require_once 'FL/DBA.php';

class FL_DBA_MySQL extends FL_DBA {
	private static $o_instance = null;
	private $rs_connection = null;	
	
	public static function getInstance(){
		if(self::$o_instance == null) 
			self::$o_instance = new self();		
		return self::$o_instance;
	}
	
	private function __construct(){
		$this->connect();
		$this->setDefaultScheme(self::$a_config[self::CONFVAR_DB_DFLT_SCH]);
	}
	public function connect(){
		$this->rs_connection = mysql_connect(
			self::$a_config[self::CONFVAR_DB_HOST], 
			self::$a_config[self::CONFVAR_DB_USER],
			self::$a_config[self::CONFVAR_DB_PASS]);
			
		if(empty($this->rs_connection)){
			throw self::getException(self::ERR_WRONG_CONFIGURATION, 'Can\'t connect to database!');
		}
	}
	
	public function disconnect(){
		mysql_close($this->rs_connection);
	}
	
	public function nextId(){
		return mysql_insert_id($this->rs_connection);
	}
	
	public function setDefaultScheme($scheme_name){
		mysql_select_db($scheme_name, $this->rs_connection);
		
		$this->_checkErrors();
	}
	
	/**
	 * Executes SQL query
	 * @see lib/FL/FL_DBA::query()
	 * @return FL_DBA_Result
	 */
	public function query($s_sql){
		$s_sql = $this->_preprocess($s_sql);
		$o_result = new FL_DBA_Result_MySQL(mysql_query($s_sql, $this->rs_connection));
		
		$this->_checkErrors();
		
		return $o_result;
	}
	
	//TODO: implement exception NO DATA FOUND
	public function queryInsert($s_table_name, $a_values){
		$s_fields = implode(',', array_keys($a_values));
		$s_values = implode(',', array_map(array($this, '_castVals'), array_values($a_values)));
		
		$s_sql = 'INSERT INTO ' . $this->_getTFN($s_table_name) 
			. '(' . $s_fields  . ')'
			. 'VALUES (' . $s_values . ')';
			
		return $this->query($s_sql);
	}
	//TODO: implement exception NO DATA FOUND
	public function queryDelete($s_table_name, $s_pk_name, $i_pk_value){
		$s_sql = 'DELETE FROM ' . $this->_getTFN($s_table_name) . '
			WHERE ' . $s_pk_name . ' = ' . $i_pk_value;
		return $this->query($s_sql);
	}
	//TODO: implement exception NO DATA FOUND
	public function queryUpdate($s_table_name, $s_pk_name, $i_pk_value, $a_values){		
		$s_sql = 'UPDATE ' . $this->_getTFN($s_table_name) . ' SET ';
		$s_set_vals = array();
		foreach ($a_values as $s_name => $m_value) {
			$s_set_vals[] = $s_name . '=' . $this->_castVals($m_value);
		}
		$s_sql .= implode(',', $s_set_vals) . 'WHERE ' . $s_pk_name . '=' . $i_pk_value;
		return $this->query($s_sql);
	}
	
	protected function _castVals($m_value){
		if(is_numeric($m_value)){
			
		}
		elseif(is_string($m_value)){
			$m_value = "'" . $this->escape($m_value) . "'";
		}
		elseif(is_bool($m_value)){
			$m_value = $m_value ? 1 : 0;
		}
		elseif(empty($m_value)){
			$m_value = 'NULL';
		}
		else{
			$m_value = "'" . $this->escape(serialize($m_value)) . "'";
		}
		return $m_value;
	}
	
	public function escape($string){
		//TODO: check magic quotes
		return addslashes($string);
	}
	
	public function unescape($string){
		//TODO: check magic quotes
		return stripslashes($string);
	}
	
	public function isTableExists($s_table_name){
		$s_scheme = self::$a_config[self::CONFVAR_DB_DFLT_SCH];
		$s_sql = 'SELECT COUNT(*) AS is_exists
			FROM information_schema.tables
			WHERE table_name = \'#' . $s_table_name . '\'
				AND table_schema = \'' . $s_scheme . '\'';
		$o_result = $this->query($s_sql);
		return $o_result->fetchAsValue('is_exists') > 0 ? true : false; 
	}
	
	private function _checkErrors(){
		$code    = mysql_errno($this->rs_connection);
		$message = mysql_error($this->rs_connection);
		
		if(!empty($code)) throw self::getException($code, $message);
	}
	
	protected function _getTFN($table_name, $schema_name = ''){
		$prefix = self::$a_config[self::CONFVAR_DB_TABLES_PREFIX];
		return ($prefix ? $prefix . self::TN_SEP : '') . $table_name;
	}
	
	protected function _preprocess($s_sql){
		$a_matches = array();
		preg_match_all('/#([0-9_a-z]+\.)?([0-9_a-z]+)/i', $s_sql, $a_matches);
		
		foreach ($a_matches[0] as $i => $val) {
			$s_sql = str_replace($a_matches[0][$i], 
				$a_matches[1][$i] . $this->_getTFN($a_matches[2][$i]), 
				$s_sql);
		}
		return $s_sql;		
	}
}

class FL_DBA_Result_MySQL extends FL_DBA_Result {
	private $rs_result = null;
	
	public function __construct($rs_result) {
		$this->rs_result = $rs_result;
	}
	
	public function fetchAllAsArray(){
		$a_result = array();
		while($a_row = mysql_fetch_array($this->rs_result, MYSQL_ASSOC)){
			$a_result[] = $a_row;
		}
		
		return $a_result;
	}
		
	public function fetchNextAsArray(){
		return mysql_fetch_array($this->rs_result, MYSQL_ASSOC);
	}
	
	public function fetchAsValue($field, $row = 0){
		return mysql_result($this->rs_result, $row, $field);
	}
}